package com.yuuzai.openworld.framework.action;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import com.yuuzai.openworld.OpenWorldObject;
import com.yuuzai.openworld.exceptions.ActionException;
import com.yuuzai.openworld.framework.IInstantiable;
import com.yuuzai.openworld.tree.ITreeObject;

public class Action<DESC extends ActionDesc<? extends Action<?>>> extends OpenWorldObject implements IInstantiable<DESC>, ITreeObject<Action<DESC>, Action<DESC>> {
	
	public boolean execute(ActionContext context) throws ActionException {
		assertNotNull(descriptor);
		boolean result = true;

		LOGGER.info(descriptor.getActionLog());
		
		ActionHandler handler = this.getHandler();
		
		if (this.children.isEmpty()) {
			assertNotNull(handler);
			result = handler.run(this, context);
		} else {
			result = this.iterateOnSubactions(handler, context);
		}
		
		LOGGER.info(descriptor.getActionLog() + " = " + result);
		return result; 
	}
	
	@SuppressWarnings("unchecked")
	protected boolean iterateOnSubactions(ActionHandler handler, ActionContext context) throws ActionException {
		assertNotNull(descriptor);
		boolean result = true;
		
		if (handler != null) {
			if (!handler.init(this, context) || !handler.execute(this, context)) {
				return false;
			}
		}
		
		Action<DESC> action = null;
		while ((action = (Action<DESC>) descriptor.getNextSubAction(currentAction)) != null) {
			if (!action.execute(context)) {
				result = false;
				break;
			}
		}
		
		if (handler == null) {
			return result;
		}
		
		return (result) ? handler.onSuccess(this, context) : handler.onFailure(this, context);
	}
	
	/* Definition */

	private DESC descriptor;

	private Action<DESC> currentAction;
	private Action<DESC> parent;
	private List<Action<DESC>> children;

	public Action() {
		this.setChildren(new ArrayList<Action<DESC>>());
	}
	
	private ActionHandler getHandler() throws ActionException {
		assertNotNull(descriptor);
		return this.descriptor.getHandler();
	}

	/* IInstantiable */

	public void setDescriptor(DESC value) {
		this.descriptor = value;
		assertNotNull(descriptor);
	}

	public DESC getDescriptor() {
		assertNotNull(descriptor);
		return this.descriptor;
	}

	/* IParentObject */

	public boolean add(Action<DESC> e) {
		return this.children.add(e);
	}

	public void add(int index, Action<DESC> element) {
		this.children.add(index, element);
	}

	public boolean addAll(Collection<? extends Action<DESC>> c) {
		return this.children.addAll(c);
	}

	public boolean addAll(int index, Collection<? extends Action<DESC>> c) {
		return this.children.addAll(index, c);
	}

	public void clear() {
		this.children.clear();
	}

	public boolean contains(Object o) {
		return this.children.contains(o);
	}

	public boolean containsAll(Collection<?> c) {
		return this.children.containsAll(c);
	}

	public Action<DESC> get(int index) {
		return this.children.get(index);
	}

	public int indexOf(Object o) {
		return this.children.indexOf(o);
	}

	public boolean isEmpty() {
		return this.children.isEmpty();
	}

	public Iterator<Action<DESC>> iterator() {
		return this.children.iterator();
	}

	public int lastIndexOf(Object o) {
		return this.children.lastIndexOf(o);
	}

	public ListIterator<Action<DESC>> listIterator() {
		return this.children.listIterator();
	}

	public ListIterator<Action<DESC>> listIterator(int index) {
		return this.children.listIterator(index);
	}

	public boolean remove(Object o) {
		return this.children.remove(o);
	}

	public Action<DESC> remove(int index) {
		return this.children.remove(index);
	}

	public boolean removeAll(Collection<?> c) {
		return this.children.removeAll(c);
	}

	public boolean retainAll(Collection<?> c) {
		return this.children.retainAll(c);
	}

	public Action<DESC> set(int index, Action<DESC> element) {
		return this.children.set(index, element);
	}

	public int size() {
		return this.children.size();
	}

	public List<Action<DESC>> subList(int fromIndex, int toIndex) {
		return this.children.subList(fromIndex, toIndex);
	}

	public Object[] toArray() {
		return this.children.toArray();
	}

	public <T> T[] toArray(T[] a) {
		return this.children.toArray(a);
	}

	public void setChildren(List<Action<DESC>> value) {
		this.children = value;
		assertNotNull(this.children);
	}

	public List<Action<DESC>> getChildren() {
		return Collections.unmodifiableList(this.children);
	}

	/* IChildObject */

	public void setParent(Action<DESC> parent) {
		this.parent = parent;
	}

	public Action<DESC> getParent() {
		return this.parent;
	}

}
